﻿using OpcUaHelper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace OPCTreeMonitor.ToPLC
{
    public class OPCPLC : IPLC
    {
        private string _name;
        private string _connectStr;
        private bool reconnected = true;
        private int _index = 0;
        private OpcUaClient client;
        public event EventHandler ReconnectStarting;
        public event EventHandler ReconnectComplete;
        public event EventHandler<string> Alive;
        public string State { get; private set; }
        public string Name => _name;
        public bool Connected => client.Connected && reconnected;
        public string ConnectStr => _connectStr;
        public int Index => _index;
        public Dictionary<string, PLCVar> PlcVarList { get; private set; } = new Dictionary<string, PLCVar>();
        public OPCPLC(string name, int index)
        {
            _name = name;
            _index = index;
            client = new OpcUaClient();

            client.ReconnectStarting += Client_ReconnectStarting;
            client.ReconnectComplete += Client_ReconnectComplete;
            client.OpcStatusChange += Client_OpcStatusChange;
        }
        private void Client_OpcStatusChange(object? sender, OpcUaStatusEventArgs e)
        {
            if (e.Error)
            {
                reconnected = false;
            }
        }
        private void Client_ReconnectStarting(object? sender, EventArgs e)
        {
            reconnected = false;
            ReconnectStarting?.Invoke(this, e);
        }
        private async void Client_ReconnectComplete(object? sender, EventArgs e)
        {
            reconnected = false; // 先标记为未完成
            try
            {
                // 等待连接服务器操作完成（确保底层会话真正建立）
                await client.ConnectServer(_connectStr);
                ReconnectComplete?.Invoke(this, e); // 触发重连完成事件
                reconnected = true;
            }
            catch (Exception ex)
            {
                reconnected = false;
            }
        }

        /// <summary>
        /// 设置 opc连接地址
        /// </summary>
        /// <param name="port">举例 "opc.tcp://192.168.10.10"</param>
        public void SetConnectionStr(string str)
        {
            _connectStr = str;
        }
        /// <summary>
        /// 不阻塞 连接与否请查看Connected属性
        /// </summary>
        public void Connect()
        {
            if (!Connected)
            {
                client.ConnectServer(_connectStr);
            }
        }
        public void DisconnectPLC()
        {
            client.Disconnect();
        }
        public void InitialPLCVarList()
        {

        }
        //object Lock = new object();
        Dictionary<string, List<string>> nodesList = new Dictionary<string, List<string>>();
        public void EnablePLCVar(string pageId, bool hasQuote, List<string> vars, bool isRead)
        {
            {
                foreach (string va in vars)
                {
                    if (!PlcVarList.ContainsKey(va))
                    {
                        var a = va;
                        var name = va.Replace("\"", "");
                        if (hasQuote)
                        {
                            var sp = va.Split(';');
                            if (sp.Count() != 2)
                            {
                                continue;
                            }
                            var stp1 = sp[1].Replace("=", "=\"");
                            var stp2 = stp1 + "\"";
                            var stp3 = stp2.Replace(".", "\".\"");
                            var stp4 = stp3.Replace("]\"", "]");
                            var stp5 = stp4.Replace("[", "\"[");
                            a = sp[0] + ";" + stp5;
                        }
                        if (!string.IsNullOrEmpty(name) && !PlcVarList.ContainsKey(name))
                        {
                            PlcVarList.Add(name, new PLCVar() { VarAddress = a });
                        }
                    }
                    if (PlcVarList.ContainsKey(va))
                    {
                        if (!nodesList.ContainsKey(pageId))
                        {
                            nodesList.Add(pageId, new List<string>());
                        }
                        if (!nodesList[pageId].Contains(va))
                        {
                            nodesList[pageId].Add(va);
                        }
                        if (isRead)
                        {
                            PlcVarList[va].IsUpdate = true;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 移除不需要刷新的点位
        /// </summary>
        /// <param name="pageId"></param>
        /// <param name="vars"></param>
        public void DisablePLCVar(string pageId, List<string> vars)
        {
            //lock (Lock)
            {
                foreach (string var in vars)
                {
                    if (PlcVarList.ContainsKey(var))
                    {
                        if (nodesList.ContainsKey(pageId))
                        {
                            if (nodesList[pageId].Contains(var))
                            {
                                nodesList[pageId].Remove(var);
                            }
                            bool isUsing = false;
                            foreach (var item in nodesList)
                            {
                                if (item.Key == pageId)
                                {
                                    continue;
                                }
                                if (item.Value != null && item.Value.Contains(var))
                                {
                                    isUsing = true;
                                    break;
                                }
                            }
                            if (!isUsing)
                            {
                                PlcVarList[var].IsUpdate = false;
                            }
                        }
                    }
                }
            }
        }
        public void SetPLCVar(string VarAddress, string value)
        {
            try
            {
                client.WriteOpcUaData(VarAddress, value);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        public void RegistorNode()
        {
            var enabled = PlcVarList.Where(x => x.Value.IsUpdate && string.IsNullOrEmpty(x.Value.RegistedAdd)).ToArray();
            if (enabled.Any())
            {
                var tlk = enabled.Take(10).ToList();
                var ids = client.RegisterNodeIds(tlk.Select(x => x.Value.VarAddress).ToList());
                for (int i = 0; i < ids.Count; i++)
                {
                    tlk[i].Value.RegistedAdd = ids[i];
                }
            }
        }
        private void Read(bool registed, List<PLCVar> plcVars)
        {
            string info = string.Empty;
            List<string> vars = new List<string>();
            if (registed)
            {
                vars = plcVars.Select(x => x.RegistedAdd).ToList();
            }
            else
            {
                vars = plcVars.Select(x => x.VarAddress).ToList();
            }
            if (vars.Any())
            {
                var reads = client.ReadOpcUaData(vars);
                for (int i = 0; i < reads.Count; i++)
                {
                    plcVars[i].Value = reads[i];
                }
            }
        }

        public void RemoveRegistedAdd()
        {
            foreach (var item in PlcVarList)
            {
                item.Value.RegistedAdd = string.Empty;
            }
        }
        public void Update()
        {
            string info = string.Empty;
            if (Connected)
            {
                var s = DateTime.Now;
                var enabled = PlcVarList.Where(x => x.Value.IsUpdate).Select(x => x.Value).ToArray();
                var hasRegister = enabled.Where(x => !string.IsNullOrEmpty(x.RegistedAdd));
                var others = enabled.Where(x => string.IsNullOrEmpty(x.RegistedAdd));
                if (hasRegister.Any())
                {
                    Read(true, hasRegister.ToList());
                }
                if (others.Any())
                {
                    Read(false, others.ToList());
                }

                info = $"{Math.Round((DateTime.Now - s).TotalMilliseconds)}ms,{enabled.Count()}vars";
            }
            else
            {
                info = "lost";
            }
            State = info;
        }

        #region IPLC接口实现
        public List<string> ReadData(List<string> vars)
        {
            return client.ReadOpcUaData(vars);
        }
        public string ReadData(string var)
        {
            return client.ReadOpcUaData(var);
        }
        public T ReadData<T>(string var)
        {
            return client.ReadNode<T>(var);
        }

        public List<string> RegisterVar(List<string> vars)
        {
            return client.RegisterNodeIds(vars);
        }

        public void UnregisterVar(List<string> vars)
        {
            client.UnregisterNodeIds(vars);
        }
        #endregion
    }
}
